﻿using System.Collections.Generic;
using Godot.core;

namespace Godot.modules.gdscript
{
    public partial class GDTokenizerText
    {
        protected const int MAX_LOOKAHEAD = 4;
        protected const int TK_RB_SIZE = MAX_LOOKAHEAD * 2 + 1;

        protected class TokenData
        {
            public GDTokenizer.Token type;
            public StringName identifier; //for identifier types
            public Variant constant; //for constant types
            public Variant.Type vtype; //for type types
            public GDFunctions.Function func; //function for built in functions
            public int line, col;

            public TokenData()
            {
                type = GDTokenizer.Token.TK_EMPTY;
                line = col = 0;
                vtype = Variant.Type.NIL;
            }
        }

        private static KeyValuePair<Variant.Type, string>[] type_list = 
        #region ____type_list____
        {
            //types
		    new KeyValuePair<Variant.Type, string>(Variant.Type.BOOL,"bool"),
            new KeyValuePair<Variant.Type, string>(Variant.Type.INT,"int"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.REAL,"float"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.STRING,"String"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.VECTOR2,"vec2"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.VECTOR2,"Vector2"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.RECT2,"Rect2"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.MATRIX32,"Matrix32"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.MATRIX32,"mat32"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.VECTOR3,"vec3"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.VECTOR3,"Vector3"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.AABB,"AABB"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.AABB,"Rect3"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.PLANE,"Plane"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.QUAT,"Quat"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.MATRIX3,"mat3"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.MATRIX3,"Matrix3"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.TRANSFORM,"trn"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.TRANSFORM,"Transform"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.COLOR,"Color"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.IMAGE,"Image"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.RID,"RID"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.OBJECT,"Object"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.INPUT_EVENT,"InputEvent"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.DICTIONARY,"dict"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.DICTIONARY,"Dictionary"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.ARRAY,"Array"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.RAW_ARRAY,"RawArray"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.INT_ARRAY,"IntArray"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.REAL_ARRAY,"FloatArray"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.STRING_ARRAY,"StringArray"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.VECTOR2_ARRAY,"Vector2Array"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.VECTOR3_ARRAY,"Vector3Array"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.COLOR_ARRAY,"ColorArray"),
			new KeyValuePair<Variant.Type, string>(Variant.Type.VARIANT_MAX, null),
        };
        #endregion

        private static KeyValuePair<GDTokenizer.Token, string>[] keyword_list =
        #region ____keyword_list____
        {
            //ops
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_OP_IN,"in"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_OP_NOT,"not"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_OP_OR,"or"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_OP_AND,"and"),
			//func
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_PR_FUNCTION,"func"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_PR_FUNCTION,"function"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_PR_CLASS,"class"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_PR_EXTENDS,"extends"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_PR_TOOL,"tool"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_PR_STATIC,"static"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_PR_EXPORT,"export"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_PR_VAR,"var"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_PR_PRELOAD,"preload"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_PR_ASSERT,"assert"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_PR_CONST,"const"),
			//controlflow
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_CF_IF,"if"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_CF_ELIF,"elif"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_CF_ELSE,"else"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_CF_FOR,"for"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_CF_WHILE,"while"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_CF_DO,"do"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_CF_SWITCH,"switch"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_CF_BREAK,"break"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_CF_CONTINUE,"continue"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_CF_RETURN,"return"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_CF_PASS,"pass"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_SELF,"self"),
			new KeyValuePair<GDTokenizer.Token, string>(GDTokenizer.Token.TK_ERROR,null)
        };
        #endregion

        private char _get_code_char(int m_ofs)
        {
            return (m_ofs + _code_pos) >= _len ? (char)0 : code_data[m_ofs + _code_pos];
        }

        private void _inc_code_pos(int m_amount)
        {
            _code_pos += m_amount; _column += m_amount;
        }

        protected void _make_token(GDTokenizer.Token p_type)
        {
            TokenData tk = _tk_rb[_tk_rb_pos];

            tk.type = p_type;
            tk.line = _line;
            tk.col = _column;

            _tk_rb_pos = (_tk_rb_pos + 1) % TK_RB_SIZE;
        }

        protected void _make_identifier(StringName p_identifier)
        {
            TokenData tk = _tk_rb[_tk_rb_pos];

            tk.type = GDTokenizer.Token.TK_IDENTIFIER;
            tk.identifier = p_identifier;
            tk.line = _line;
            tk.col = _column;

            _tk_rb_pos = (_tk_rb_pos + 1) % TK_RB_SIZE;
        }

        protected void _make_built_in_func(GDFunctions.Function p_func)
        {
            TokenData tk = _tk_rb[_tk_rb_pos];

            tk.type = GDTokenizer.Token.TK_BUILT_IN_FUNC;
            tk.func = p_func;
            tk.line = _line;
            tk.col = _column;

            _tk_rb_pos = (_tk_rb_pos + 1) % TK_RB_SIZE;
        }

        protected void _make_constant(Variant p_constant)
        {
            TokenData tk = _tk_rb[_tk_rb_pos];

            tk.type = GDTokenizer.Token.TK_CONSTANT;
            tk.constant = p_constant;
            tk.line = _line;
            tk.col = _column;

            _tk_rb_pos = (_tk_rb_pos + 1) % TK_RB_SIZE;
        }

        protected void _make_type(Variant.Type p_type)
        {
            TokenData tk = _tk_rb[_tk_rb_pos];

            tk.type = GDTokenizer.Token.TK_BUILT_IN_TYPE;
            tk.vtype = p_type;
            tk.line = _line;
            tk.col = _column;

            _tk_rb_pos = (_tk_rb_pos + 1) % TK_RB_SIZE;
        }

        protected void _make_error(string p_error)
        {
            _error_flag = true;
            _last_error = p_error;

            TokenData tk = _tk_rb[_tk_rb_pos];
            tk.type = GDTokenizer.Token.TK_ERROR;
            tk.constant = p_error;
            tk.line = _line;
            tk.col = _column;
            _tk_rb_pos = (_tk_rb_pos + 1) % TK_RB_SIZE;
        }

        protected void _make_newline(int p_spaces)
        {
            TokenData tk = _tk_rb[_tk_rb_pos];
            tk.type = GDTokenizer.Token.TK_NEWLINE;
            tk.constant = p_spaces;
            tk.line = _line;
            tk.col = _column;
            _tk_rb_pos = (_tk_rb_pos + 1) % TK_RB_SIZE;
        }

        protected void _advance()
        {
            if (_error_flag)
            {
                //parser broke
                _make_error(_last_error);
                return;
            }

            if (_code_pos >= _len)
            {
                _make_token(GDTokenizer.Token.TK_EOF);
                return;
            }

            while (true)
            {
                bool is_node_path = false;

                switch (_get_code_char(0))
                {
                    case '\0':  {
                        _make_token(GDTokenizer.Token.TK_EOF);                          
                    } break;

                    case '\t':  /**/
                    case '\r':  /**/
                    case ' ':   {
                        _inc_code_pos(1);                        
                    } continue;

                    case '\n':  {
                        _line++;
                        _inc_code_pos(1);
                        _column = 0;
                        int i = 0;
                        for (i = 0; _get_code_char(i) == ' ' || _get_code_char(i) == '\t'; i++) ;
                        _make_newline(i);                           
                    } return;

                    case '#':   { // line comment skip
                        while (_get_code_char(0) != '\n')
                        {
                            _code_pos++;
                            if (_get_code_char(0) == '\0') //end of file
                            {
                                _make_error("Unterminated Comment");
                                return;
                            }
                        }
                        _inc_code_pos(1);
                        _column = 0;
                        _line++;

                        int i = 0;
                        for (i = 0; _get_code_char(i) == ' ' || _get_code_char(i) == '\t'; i++);
                        _make_newline(i);
                    } return;

                    case '/':   {
                        switch (_get_code_char(1))
                        {
                            case '=':   { // div-equal
                                _make_token(GDTokenizer.Token.TK_OP_ASSIGN_DIV);
                                _inc_code_pos(1);
                            } break;

                            default:    {
                                _make_token(GDTokenizer.Token.TK_OP_DIV);
                            } break;
                        }                        
                    } break;

                    case '=':   {
                        if (_get_code_char(1) == '=') // equality
                        {
                            _make_token(GDTokenizer.Token.TK_OP_EQUAL);
                            _inc_code_pos(1);
                        }
                        else // assignment
                        {
                            _make_token(GDTokenizer.Token.TK_OP_ASSIGN);
                        }                        
                    } break;

                    case '<':   {
                        if (_get_code_char(1) == '=')
                        {
                            _make_token(GDTokenizer.Token.TK_OP_LESS_EQUAL);
                            _inc_code_pos(1);
                        }
                        else if (_get_code_char(1) == '<')
                        {
                            if (_get_code_char(2) == '=')
                            {
                                _make_token(GDTokenizer.Token.TK_OP_ASSIGN_SHIFT_LEFT);
                                _inc_code_pos(1);
                            }
                            else
                            {
                                _make_token(GDTokenizer.Token.TK_OP_SHIFT_LEFT);
                            }
                            _inc_code_pos(1);
                        }
                        else
                        {
                            _make_token(GDTokenizer.Token.TK_OP_LESS);
                        }                        
                    } break;

                    case '>':   {
                        if (_get_code_char(1) == '=')
                        {
                            _make_token(GDTokenizer.Token.TK_OP_GREATER_EQUAL);
                            _inc_code_pos(1);
                        }
                        else if (_get_code_char(1) == '>')
                        {
                            if (_get_code_char(2) == '=')
                            {
                                _make_token(GDTokenizer.Token.TK_OP_ASSIGN_SHIFT_RIGHT);
                                _inc_code_pos(1);
                            }
                            else
                            {
                                _make_token(GDTokenizer.Token.TK_OP_SHIFT_RIGHT);
                            }
                            _inc_code_pos(1);
                        }
                        else
                        {
                            _make_token(GDTokenizer.Token.TK_OP_GREATER);
                        }                     
                    } break;

                    case '!':   {
                        if (_get_code_char(1) == '=')
                        {
                            _make_token(GDTokenizer.Token.TK_OP_NOT_EQUAL);
                            _inc_code_pos(1);
                        }
                        else
                        {
                            _make_token(GDTokenizer.Token.TK_OP_NOT);
                        }                       
                    } break;

                    case '{':   {
                        _make_token(GDTokenizer.Token.TK_CURLY_BRACKET_OPEN);
                    } break;

                    case '}':   {
                        _make_token(GDTokenizer.Token.TK_CURLY_BRACKET_CLOSE);
                    } break;

                    case '[':   {
                        _make_token(GDTokenizer.Token.TK_BRACKET_OPEN);
                    } break;

                    case ']':   {
                        _make_token(GDTokenizer.Token.TK_BRACKET_CLOSE);
                    } break;

                    case '(':   {
                        _make_token(GDTokenizer.Token.TK_PARENTHESIS_OPEN);
                    } break;

                    case ')':   {
                        _make_token(GDTokenizer.Token.TK_PARENTHESIS_CLOSE);
                    } break;
                    
                    case ',':   {
                        _make_token(GDTokenizer.Token.TK_COMMA);
                    } break;

                    case ';':   {
                        _make_token(GDTokenizer.Token.TK_SEMICOLON);
                    } break;

                    case '?':   {
                        _make_token(GDTokenizer.Token.TK_QUESTION_MARK);
                    } break;

                    case ':':   {
                        _make_token(GDTokenizer.Token.TK_COLON); //for methods maybe but now useless.
                    } break;

                    case '^':   {
                        if (_get_code_char(1) == '=')
                        {
                            _make_token(GDTokenizer.Token.TK_OP_ASSIGN_BIT_XOR);
                            _inc_code_pos(1);
                        }
                        else
                        {
                            _make_token(GDTokenizer.Token.TK_OP_BIT_XOR);
                        }
                    } break;

                    case '~':   {
                        _make_token(GDTokenizer.Token.TK_OP_BIT_INVERT);
                    } break;

                    case '&':   {
                        if (_get_code_char(1) == '&')
                        {

                            _make_token(GDTokenizer.Token.TK_OP_AND);
                            _inc_code_pos(1);
                        }
                        else if (_get_code_char(1) == '=')
                        {
                            _make_token(GDTokenizer.Token.TK_OP_ASSIGN_BIT_AND);
                            _inc_code_pos(1);
                        }
                        else
                        {
                            _make_token(GDTokenizer.Token.TK_OP_BIT_AND);
                        }
                    } break;

                    case '|':   {
                        if (_get_code_char(1) == '|')
                        {
                            _make_token(GDTokenizer.Token.TK_OP_OR);
                            _inc_code_pos(1);
                        }
                        else if (_get_code_char(1) == '=')
                        {
                            _make_token(GDTokenizer.Token.TK_OP_ASSIGN_BIT_OR);
                            _inc_code_pos(1);
                        }
                        else
                        {
                            _make_token(GDTokenizer.Token.TK_OP_BIT_OR);
                        }
                    } break;

                    case '*':   {
                        if (_get_code_char(1) == '=')
                        {
                            _make_token(GDTokenizer.Token.TK_OP_ASSIGN_MUL);
                            _inc_code_pos(1);
                        }
                        else
                        {
                            _make_token(GDTokenizer.Token.TK_OP_MUL);
                        }
                    } break;

                    case '+':   {
                        if (_get_code_char(1) == '=')
                        {
                            _make_token(GDTokenizer.Token.TK_OP_ASSIGN_ADD);
                            _inc_code_pos(1);
                        }
                        else
                        {
                            _make_token(GDTokenizer.Token.TK_OP_ADD);
                        }
                    } break;

                    case '-':   {
                        if (_get_code_char(1) == '=')
                        {
                            _make_token(GDTokenizer.Token.TK_OP_ASSIGN_SUB);
                            _inc_code_pos(1);
                        }
                        else
                        {
                            _make_token(GDTokenizer.Token.TK_OP_SUB);
                        }
                    } break;

                    case '%':   {
                        if (_get_code_char(1) == '=')
                        {
                            _make_token(GDTokenizer.Token.TK_OP_ASSIGN_MOD);
                            _inc_code_pos(1);
                        }
                        else
                        {
                            _make_token(GDTokenizer.Token.TK_OP_MOD);
                        }
                    } break;

                    case '@':   {
                        if (_get_code_char(1) != '"')
                        {
                            _make_error("Unexpected '@'");
                            return;
                        }
                        _inc_code_pos(1);
                        is_node_path = true;
                        goto case '"';
                   } break;

                    case '"':   {
                        int i = 1;
                        string str = "";
                        
                        while (true)
                        {
                            if (_get_code_char(i) == '\0')
                            {
                                _make_error("Unterminated String");
                                return;
                            }
                            else if (_get_code_char(i) == '"')
                            {
                                break;
                            }
                            else if (_get_code_char(i) == '\\')
                            {
                                //escaped characters...
                                i++;
                                char next = _get_code_char(i);
                                if (next == '\0')
                                {
                                    _make_error("Unterminated String");
                                    return;
                                }

                                char res = '\0';

                                switch (next)
                                {
                                    case 'a': res = (char)7; break;
                                    case 'b': res = (char)8; break;
                                    case 't': res = (char)9; break;
                                    case 'n': res = (char)10; break;
                                    case 'v': res = (char)11; break;
                                    case 'f': res = (char)12; break;
                                    case 'r': res = (char)13; break;
                                    case '\'': res = '\''; break;
                                    case '\"': res = '\"'; break;
                                    case '\\': res = '\\'; break;
                                    case 'x':   {
                                        //hexnumbarh - oct is deprecated
                                        int read = 0;
                                        for (int j = 0; j < 4; j++)
                                        {
                                            char c = _get_code_char(i + j);
                                            if (c == '\0')
                                            {
                                                _make_error("Unterminated String");
                                                return;
                                            }

                                            if (!c.is_alpha_numeric())
                                            {
                                                if (j == 0 || ((short)(j) & 1) != 0)
                                                {
                                                    _make_error("Malformed hex constant in string");
                                                    return;
                                                }
                                                else
                                                    break;
                                            }

                                            char v;
                                            if (c >= '0' && c <= '9')
                                            {
                                                v = (char)(c - '0');
                                            }
                                            else if (c >= 'a' && c <= 'f')
                                            {
                                                v = (char)(c - 'a');
                                                v += (char)10;
                                            }
                                            else if (c >= 'A' && c <= 'F')
                                            {
                                                v = (char)(c - 'A');
                                                v += (char)10;
                                            }
                                            else
                                            {
                                                v = (char)0;
                                            }

                                            res <<= 4;
                                            res |= v;

                                            read++;
                                        }
                                        i += read - 1;
                                    } break;

                                    default:    {
                                        _make_error("Invalid escape sequence");
                                    } return;
                                }

                                str += res;
                            }
                            else
                            {
                                str += _get_code_char(i);
                            }
                            i++;
                        }
                        _inc_code_pos(i);

                        if (is_node_path)
                        {
                            _make_constant(new NodePath(str));
                        }
                        else
                        {
                            _make_constant(str);
                        }
                        break;
                    }

                    default:    {
                        if (_get_code_char(0).is_numeric() || (_get_code_char(0).Equals('.') && _get_code_char(1).is_numeric()))
                        {
                            // parse number
                            bool period_found = false;
                            bool exponent_found = false;
                            bool hexa_found = false;
                            bool sign_found = false;

                            string str = "";
                            int i = 0;

                            while (true)
                            {
                                if (_get_code_char(i) == '.')
                                {
                                    if (period_found || exponent_found)
                                    {
                                        _make_error("Invalid numeric constant at '.'");
                                        return;
                                    }
                                    period_found = true;
                                }
                                else if (_get_code_char(i) == 'x')
                                {
                                    if (hexa_found || str.Length != 1 || !((i == 1 && str[0] == '0') || (i == 2 && str[1] == '0' && str[0] == '-')))
                                    {
                                        _make_error("Invalid numeric constant at 'x'");
                                        return;
                                    }
                                    hexa_found = true;
                                }
                                else if (!hexa_found && _get_code_char(i) == 'e')
                                {
                                    if (hexa_found || exponent_found)
                                    {
                                        _make_error("Invalid numeric constant at 'e'");
                                        return;
                                    }
                                    exponent_found = true;
                                }
                                else if (_get_code_char(i).is_numeric())
                                {
                                    //all ok
                                }
                                else if (hexa_found && _get_code_char(i).is_alpha_numeric())
                                {

                                }
                                else if ((_get_code_char(i) == '-' || _get_code_char(i) == '+') && exponent_found)
                                {
                                    if (sign_found)
                                    {
                                        _make_error("Invalid numeric constant at '-'");
                                        return;
                                    }
                                    sign_found = true;
                                }
                                else
                                {
                                    break;
                                }

                                str += _get_code_char(i);
                                i++;
                            }

                            if (!(str[str.Length - 1].is_numeric() || ( hexa_found && str[str.Length - 1].is_alpha_numeric() ) ))
                            {
                                _make_error("Invalid numeric constant: " + str);
                                return;
                            }

                            _inc_code_pos(str.Length);
                            if (hexa_found)
                            {
                                int val = str.hex_to_int();
                                _make_constant(val);
                            }
                            else if (period_found)
                            {
                                double val = double.Parse(str);
                                //print_line("*%*%*%*% to convert: "+str+" result: "+rtos(val));
                                _make_constant(val);
                            }
                            else
                            {
                                int val = int.Parse(str);
                                _make_constant(val);

                            }

                            return;
                        }

                        if (_get_code_char(0) == '.')
                        {
                            //parse period
                            _make_token(GDTokenizer.Token.TK_PERIOD);
                            break;
                        }

                        if (_get_code_char(0).is_alphabet())
                        {
                            // parse identifier
                            string str = "";
                            str += _get_code_char(0);

                            int i = 1;
                            while (_get_code_char(i).is_alphabet())
                            {
                                str += _get_code_char(i);
                                i++;
                            }

                            bool identifier = false;

                            if (str == "null")
                            {
                                _make_constant(new Variant());
                            }
                            else if (str == "true")
                            {
                                _make_constant(true);
                            }
                            else if (str == "false")
                            {
                                _make_constant(false);
                            }
                            else
                            {
                                bool found = false;

                                int idx = 0;

                                while (type_list[idx].Value != null)
                                {
                                    if (str == type_list[idx].Value)
                                    {
                                        _make_type(type_list[idx].Key);
                                        found = true;
                                        break;
                                    }
                                    idx++;
                                }

                                if (!found)
                                {
                                    //built in func?
                                    for (int count = 0; count < (int)GDFunctions.Function.FUNC_MAX; count++)
                                    {
                                        if (str == GDFunctions.get_func_name((GDFunctions.Function)count))
                                        {
                                            _make_built_in_func((GDFunctions.Function)count);
                                            found = true;
                                            break;
                                        }
                                    }

                                    //keyword
                                }

                                if (!found)
                                {
                                    idx = 0;

                                    found = false;

                                    while (keyword_list[idx].Value != null)
                                    {
                                        if (str == keyword_list[idx].Value)
                                        {
                                            _make_token(keyword_list[idx].Key);
                                            found = true;
                                            break;
                                        }
                                        idx++;
                                    }
                                }

                                if (!found)
                                    identifier = true;
                            }

                            if (identifier)
                            {
                                _make_identifier(str);
                            }

                            _inc_code_pos(str.Length);
                            return;
                        }
                        _make_error("Unknown character");                        
                    } return;
                }
                
                _inc_code_pos(1);
                break;
            }
        }
    }
}
